Об`єктно-орієнтоване програмування на Borland C

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Об'єктно-орієнтоване програмування на Borland C + +

Тексти лекцій, прочитаних в Московському державному університеті економіки, статистики та інформатики

У текстах лекцій розглянуті основні принципи та засоби об'єктно-орієнтованого програмування з застосуванням мови С + + і системи програмування Borland C + +. У додатку наведено задачі для самостійного рішення. Передбачається, що читач вже знайомий з основами універсальних мов програмування. Посібник призначений для студентів спеціальності "Прикладна математика" і може бути використано студентами інших спеціальностей для самостійного вивчення С + +.

1. Об'єктно-орієнтований підхід у програмуванні

1.1 Технології програмування

Технологія програмування - це сукупність методів і засобів розробки (написання) програм та порядок застосування цих методів та засобів.

На ранніх етапах розвитку програмування, коли програми писалися у вигляді послідовностей машинних команд, будь-яка технологія програмування була відсутня. Перші кроки в розробці технології полягали в поданні програми у вигляді послідовності операторів. Написанню послідовності машинних команд передувало складання операторної схеми, що відбиває послідовність операторів і переходи між ними. Операторний підхід дозволив розробити перші програми для автоматизації складання програм - так звані складові програми.

Зі збільшенням розмірів програм стали виділяти їх відокремлені частини і оформляти їх як підпрограми. Частина таких підпрограм об'єднувалася у бібліотеки, з яких подпрошрамми можна було включати в робочі програми і потім викликати з робочих програм. Це поклало початок процедурного програмування - велика програма представлялася сукупністю процедур-підпрограм. Одна з підпрограм була головною, і з неї починалося виконання програми.

У 1958 році були розроблені перші мови програмування, Фортран та Алгол-58. Програма на Фортрані складалася з головної програми і деякої кількості процедур - підпрограм і функцій. Програма на Алголь-58 і його наступної версії Алголь-60 представляла собою єдине ціле, але мала блокову структуру, що включає головний блок і вкладені блоки підпрограм і функцій. Компілятори для Фортрану забезпечували роздільну трансляцію процедур і наступне їх об'єднання в робочу програму, перші компілятори для Алгола припускали, що транслюється відразу вся програма, роздільна трансляція процедур не забезпечувалася.

Процедурний підхід зажадав структурування майбутньої програми, поділу її на окремі процедури. При розробці окремої процедури про інших процедурах потрібно знати тільки їх призначення та спосіб виклику. З'явилася можливість переробляти окремі процедури, не зачіпаючи іншої частини програми, скорочуючи при цьому витрати і машинного часу на розробку і модернізацію програм.

Наступним кроком у поглибленні структурування програм стало так зване структурне програмування, при якому програма в цілому і окремі процедури розглядалися як послідовності канонічних структур: лінійних ділянок, циклів і розгалужень. З'явилася можливість читати і перевіряти програму як послідовний текст, що підвищило продуктивність праці програмістів при розробці і налагодженню програм. З метою підвищення структурності програми були висунуті вимоги до більшої незалежності підпрограм, підпрограми повинні зв'язуватися з зухвалими їх програмами тільки шляхом передачі їм аргументів, використання в підпрограмах змінних, що належать іншим процедурам або головній програмі, стало вважатися небажаним.

Процедурне та структурне програмування торкнулися, перш за все, процес опису алгоритму як послідовності кроків, що ведуть від варійованих вихідних даних до шуканого результату. Для вирішення спеціальних завдань стали розроблятися мови програмування, орієнтовані на конкретний клас задач: на системи управління базами даних, імітаційне моделювання тощо

При розробці трансляторів все більше уваги стало приділятися виявленню помилок у вихідних текстах програм, забезпечуючи цим скорочення витрат часу на налагодження програм.

Застосування програм в самих різних областях людської діяльності призвело до необхідності підвищення надійності всього програмного забезпечення. Одним з напрямків вдосконалення мов програмування стало підвищення рівня типізації даних. Теорія типів даних виходить з того, що кожне використовуване у програмі дане належить одному і тільки одному типу даних. Тип даного визначає безліч можливих значень даного і набір операцій, допустимих над цими даними. Дане конкретного типу в ряді випадків може бути перетворено в даний іншого типу, але таке перетворення має бути явно представлено в програмі. У залежності від ступеня виконання перерахованих вимог можна говорити про рівень типізації тієї чи іншої мови програмування. Прагнення підвищити рівень типізації мови програмування призвело до появи мови Паскаль, який вважається суворо типізовані мовою, хоча й у ньому дозволені деякі неявні перетворення типів, наприклад, цілого в реальне. Застосування суворо типізованого мови при написанні програми дозволяє ще при трансляції вихідного тексту виявити багато помилок використання даних і цим підвищити надійність програми. Разом з тим строга типізація сковувала свободу програміста, ускладнювала застосування деяких прийомів перетворення даних, що часто використовуються в системному програмуванні. Практично одночасно з Паскалем була розроблена мова Сі, більшою мірою орієнтований на системне програмування та відноситься до слабо типізовані мов.

Всі універсальні мови програмування, незважаючи на відмінності в синтаксисі і використовуваних ключових словах, реалізують одні й ті ж канонічні структури: оператори присвоювання, цикли і розгалуження. У всіх сучасних мовах присутні зумовлені (базові) типи даних (цілі і речові арифметичні типи, символьний і, можливо, рядковий тип), є можливість використання агрегатів даних, в тому числі масивів і структур (записів). Для арифметичних даних дозволені звичайні арифметичні операції, для агрегатів даних зазвичай передбачена тільки операція присвоювання і можливість звернення до елементів агрегату. Разом з тим при розробці програми для вирішення конкретної прикладної задачі бажана можливо велика концептуальна близькість тексту програми до опису завдання. Наприклад, якщо рішення задачі вимагає виконання операцій над комплексними числами або квадратними матрицями, бажано, щоб у програмі явно були присутні оператори додавання, віднімання, множення і ділення даних типу комплексного числа, додавання, віднімання, множення і обігу даних типу квадратної матриці. Вирішення цієї проблеми можливо кількома шляхами:

- Побудовою мови програмування, що містить як можна більше типів даних, і вибором для кожного класу задач деякого підмножини цієї мови. Така мова іноді називають мовою-оболонкою. На роль мови-оболонки претендував мова ПЛ / 1, що виявився настільки складним, що так і не вдалося побудувати його формалізований опис. Відсутність формалізованого опису, однак, не завадило широкому застосуванню ПЛ / 1 як у Західній Європі, так і в СРСР.

- Побудовою розширюваної мови, що містить невелике ядро і допускає розширення, доповнює мову типами даних і операторами, відображають концептуальну сутність конкретного класу задач. Така мова називають мовою-ядром. Як мова-ядро були розроблені мови Симула і Алгол-68, що не набули широкого поширення, але що зробили великий вплив на розробку інших мов програмування.

Подальшим розвитком другого шляху з'явився об'єктно-орієнтований підхід до програмування, що розглядається в наступному параграфі.

1.2. Сутність об'єктно-орієнтованого підходу до програмування

Основні ідеї об'єктно-орієнтованого підходу спираються на наступні положення:

- Програма представляє собою модель деякого реального процесу, частини реального світу.

- Модель реального світу або її частини може бути описана як сукупність взаємодіючих між собою об'єктів.

- Об'єкт описується набором параметрів, значення яких визначають стан об'єкта, і набором операцій (дій), які може виконувати об'єкт.

- Взаємодія між об'єктами здійснюється посилкою спеціальних повідомлень від одного об'єкта до іншого. Повідомлення, отримане об'єктом, може зажадати виконання певних дій, наприклад, зміни стану об'єкта.

- Об'єкти, описані одним і тим же набором параметрів і здатні виконувати один і той самий набір дій є клас однотипних об'єктів.

З точки зору мови програмування клас об'єктів можна розглядати як тип даного, а окремий об'єкт - як дане цього типу. Визначення програмістом власних класів об'єктів для конкретного набору завдань має дозволити описувати окремі завдання у термінах самого класу завдань (при відповідному виборі імен типів та імен об'єктів, їх параметрів і виконуваних дій).

Таким чином, об'єктно-орієнтований підхід передбачає, що при розробці програми повинні бути визначені класи використовуються у програмі об'єктів і побудовані їхні описи, потім створені екземпляри необхідних об'єктів і визначено взаємодію між ними.

Класи об'єктів часто зручно будувати так, щоб вони утворювали ієрархічну структуру. Наприклад, клас "Студент", що описує абстрактного студента, може служити основою для побудови класів "Студент 1 курсу", "Студент 2 курсу" і т.д., які мають всі властивості студента взагалі і деякими додатковими властивостями, що характеризують студента конкретного курсу. При розробці інтерфейсу з користувачем програми можуть використовувати об'єкти загального класу "Вікно" і об'єкти класів спеціальних вікон, наприклад, вікон інформаційних повідомлень, вікон введення даних і т.п. У таких ієрархічних структурах один клас може розглядатися як базовий для інших, похідних від нього класів. Об'єкт похідного класу має всі властивості базового класу і деякими власними властивостями, він може реагувати на ті ж типи повідомлень від інших об'єктів, що й об'єкт базового класу і на повідомлення, які мають сенс тільки для похідного класу. Зазвичай кажуть, що об'єкт похідного класу успадковує всі властивості свого базового класу.

Деякі параметри об'єкта можуть бути локалізовані всередині об'єкта і недоступні для прямого впливу ззовні об'єкта. Наприклад, під час руху об'єкта-автомобіля об'єкт-водій може впливати лише на обмежений набір органів управління (рульове колесо, педалі газу, зчеплення і гальма, важіль перемикання передач) і йому недоступний цілий ряд параметрів, що характеризують стан двигуна і автомобіля в цілому.

Очевидно, для того, щоб продуктивно застосовувати об'єктний підхід для розробки програм, необхідні мови програмування, що підтримують цей підхід, тобто дозволяють будувати опис класів об'єктів, утворювати дані об'єктних типів, виконувати операції над об'єктами. Одним з перших таких мов стала мова SmallTalk в якому всі дані є об'єктами деяких класів, а загальна система класів будується як ієрархічна структура на основі визначених базових класів.

Досвід програмування показує, що будь-який методичний підхід в технології програмування не повинен застосовуватися сліпо з ігноруванням інших підходів. Це відноситься і до об'єктно-орієнтованого підходу. Існує ряд типових проблем, для яких його корисність найбільш очевидна, до таких проблем відносяться, зокрема, завдання імітаційного моделювання, програмування діалогів з користувачем. Існують і завдання, в яких застосування об'єктного підходу ні до чого, крім зайвих витрат праці, не приведе. У зв'язку з цим найбільшого поширення набули об'єктно-орієнтовані мови програмування, що дозволяють поєднувати об'єктний підхід з іншими методологіями. У деяких мовах і системах програмування застосування об'єктного підходу обмежується засобами інтерфейсу з користувачем (наприклад, Visual FoxPro ранніх версій).

Найбільш використовуваними в даний час об'єктно-орієнтованими мовами є Паскаль з об'єктами і Сі + +, причому найбільш розвинені засоби для роботи з об'єктами містяться в Сі + +.

Практично всі об'єктно-орієнтовані мови програмування є країнами, що розвиваються мовами, їх стандарти регулярно уточнюються і розширюються. Наслідком цього розвитку є неминучі відмінності у вхідних мовами компіляторів різних систем програмування. . Найбільш поширеними в даний час є системи програмування Microsoft C + +, Microsoft Visual C + + і системи програмування фірми Borland International. Подальший матеріал в даному посібнику викладається стосовно до системи програмування Borland C + +. Це пов'язано перш за все наявністю у цій системі програмування розвиненою інтегрованого середовища, яка об'єднує текстовий редактор, компілятор, редактор зв'язків (компонувальник) і налагоджувальні кошти.

2. Початкові відомості про мову Сі

2.1 Призначення Сі, історичні відомості

Мова Сі був розроблений в 70-і роки як мова системного програмування. При цьому ставилося завдання отримати мову, забезпечує реалізацію ідей процедурного і структурного програмування і можливість реалізації специфічних прийомів системного програмування. Така мова дозволив би розробляти складні програми на рівні, порівнянному з програмуванням на Асемблері, але істотно швидше. Ці цілі, в основному, були досягнуті. Більшість компіляторів для Сі написані на Сі, операційна система UNIX <також майже повністю написана на Сі. Недоліком Сі виявилася низька надійність розроблювальних програм із-за відсутності контролю типів. Спроба виправити становище включенням до системи програмування Сі окремої програми, яка контролює неявні перетворення типів, вирішила цю проблему лише частково.

На основі Сі в 80-ті роки була розроблена мова Сі + +, спочатку названий "Сі з класами". Сі + + практично включає мову Сі і доповнений засобами об'єктно-орієнтованого програмування. Робоча версія Сі + + з'явилася в 1983 р. З тих пір мова продовжує розвиватися і опубліковано кілька версій проекту стандартів Сі і Сі + +.

Поруч фірм, що виробляють програмне забезпечення, розроблені компілятори для Сі і Сі + +. Системи програмування фірми Borland International виділяються серед інших фірм насамперед комплексним підходом до розробки програм, зреалізований у включенні до системи програмування інтегрованого середовища розробника, яка об'єднує під загальним управлінням текстовий редактор для введення вихідних текстів програм, компілятор, редактор зв'язків і набір налагоджувальних засобів. У 1989 р. цією фірмою була випущена система Turbo C + +, що включала компілятор Сі + +, що працює в операційній системі DOS, з 1992 р. випускаються системи Borland C + +, що містять компілятори С + + для DOS та WINDOWS, з 1997 р. поставляється версія Borland C 5.0, що містить компілятори С + + для WINDOWS, причому компілятор для WINDOWS тепер дозволяє розробляти як 16-розрядні, так і 32-розрядні варіанти програм для ПЕОМ з процесорами i486 та Pentium.

Програма на Сі / Сі + + являє собою один або кілька вихідних файлів, які можуть транслюватися роздільно. Результати трансляції (об'єктні файли) об'єднуються у виконуваний файл редактором зв'язків (компонувальником). Зазвичай розрізняють два типи вихідних файлів: файли заголовків і програмні файли. Файли заголовків містять описи типів даних і прототипів функцій і призначені для включення в програмні файли перед їх компіляцією, їх імена, як правило, мають розширення. H, наприклад, stdio.h. Програмні файли містять описи функцій і, можливо, глобальних змінних і констант, їх імена прийнято записувати з розширеннями. C або. Cpp, наприклад, myprog.cpp. Один і той же файл заголовків може включатися в кілька програмних файлів

Кожен файл містить послідовність так званих "зовнішніх визначень", що описують типи даних, змінні, константи та функції.

У наступних параграфах цього розділу наведено огляд засобів Сі / Сі + +, не пов'язаних з об'єктною орієнтацією Сі + +.

2.2 Алфавіт, базові типи і опис даних.

Алфавіт мови включає практично всі символи, наявні на стандартній клавіатурі ПЕОМ:

- Латинські літери A. .. Z, a. .. z;

- Цифри 0 ... 9;

- Знаки операцій та роздільники:

{} [] (). , -> & * + - ~! /%? :; = <> | # ^

Деякі операції позначаються комбінаціями символів, значення символів операцій у ряді випадків залежать від контексту, в якому вони вжиті.

Базові (зумовлені) типи даних об'єднані в дві групи: дані цілого типу і дані з плаваючою точкою (речові).

Дані цілого типу можуть бути звичайними цілими зі знаком (signed) і цілими без знаку (unsigned). За кількістю розрядів, використовуваних для подання даного (діапазону значень) розрізняють звичайні цілі (int), короткі цілі (short int) і довгі цілі (long int). Символьні дані (char) також розглядаються як цілі і можуть бути зі знаком і без знаку.

Константи цілого типу записуються як послідовності десяткових цифр, тип константи залежить від числа цифр у записі константи і може бути уточнений додаванням в кінці константи букв L або l (тип long), U або u (тип unsigned) або їх поєднання:

321 - константа типу int,

5326u - константа типу unsigned int,

45637778 - константа типу long int,

2746L - константа типу long int.

Цілі константи можуть записуватися в вісімковій системі числення, в цьому випадку першою цифрою повинна бути цифра 0, число може містити тільки цифри 0 ... 7:

0777 - константа типу int,

0453377 - константа типу long.

Цілі константи можна записувати і у шістнадцятковій системі числення, в цьому випадку запис константи починається з символів 0x або 0X:

0x45F - константа типу int,

0xFFFFFFFF - константа типу unsigned long.

Константи типу char завжди полягають в одиночні лапки, значення константи задається або знаком з використовуваного набору символів, або цілої константою, якій передує зворотна коса риса: 'A', '\ 33', '\ 042', '\ x1B'. Є також ряд спеціальних символів, які можуть зазначатися в якості значень константи типу char:

'\ N' - новий рядок,

'\ T' - горизонтальна табуляція,

'\ V' - вертикальна табуляція,

'\ R' - переклад каретки,

'\ F' - переклад сторінки,

'\ A' - звуковий сигнал,

'\''- Одиночна лапка (апостроф),

'\ "' - Лапки,

'\ \' - Зворотна коса риска.

Дійсні числа можуть бути значеннями одного з трьох типів: float, double, long double. Діапазон значень кожного з цих типів залежить від використовуваних ЕОМ і компілятора. Константи речових типів можуть записуватися у природному чи експоненційної формах і за замовчуванням мають тип double, наприклад, 15.31, 1.43E-3, 2345.1e4. При необхідності тип константи можна уточнити, записавши в кінці суфікс f або F для типу float, суфікс l або L для типу long double.

Зовнішнє визначення, що оголошує змінні, складається з необов'язкового специфікатора класу пам'яті, специфікатором типу та списку так званих деклараторів-ініціалізаторов, кожен з яких оголошує ідентифікатор однієї змінної і, можливо, значення, що привласнюється змінної при її оголошенні. Зовнішнє визначення закінчується крапкою з комою:

int i, j, k; / / Три змінних типу int без явної ініціалізації

double x = 1, y = 2; / / Дві змінних типу double з початковими значеннями 1 і 2

char c1 = 0 "; / / Змінна типу char, її значення - код літери 0

Текст, записаний у цих прикладах після знаків / /, є коментарем і служить тільки для документування програми. Такий коментар може займати тільки один рядок тексту й допускається в текстах програм на Сі + +. Коментар, що займає кілька рядків, полягає в спеціальні дужки / * і * /.

Як специфікатором класу пам'яті в зовнішньому визначенні може зазначатися одне з ключових слів extern, static або typedef, Специфікатор extern означає, що оголошений об'єкт належить іншому програмному файлу, а тут дається інформація про його ім'я і тип і не повинно бути присутнім ініціалізували вираз. Специфікатор static обмежує область дії декларованого імені даним файлом або блоком, якщо оголошення міститься в блоці.

Якщо оголошення даного міститься всередині тіла функції (локальне оголошення), то можна вказувати специфікатора класу пам'яті register або auto. Специфікатор register носить рекомендаційний характер, компілятор намагається розмістити дане цей класу в регістрі процесора, якщо в даний момент є вільні регістри. Специфікатор auto приймається за замовчуванням і тому явно не вказується, він означає, що дане класу auto повинен розміщуватися в програмному стеку при виклику функції.

Специфікатор typedef служить для присвоєння імені описуваного типу даного і буде розглянуто докладніше в наступному параграфі.

Поряд з показаними вище константами-літералами, значення яких визначаються їх поданням в програмі, в Сі і Сі + + передбачені константи, яким присвоюються власні імена - іменовані константи. В описі іменованої константи присутній описувач const, наприклад,

const double Pi = 3.141592653;

Змінної, ідентифікатор якої оголошено з описувачем const, не можна привласнити інше значення, ніж було встановлено при оголошенні ідентифікатора. Ініціалізують значення при оголошенні константи є обов'язковим.

Поряд з базовими цілими і речовими типами різних розмірів у програмі можуть оголошуватися і використовуватися дані типів, що визначаються програмістом: покажчики, посилання, агрегати даних і дані перечислимого типу.

Перераховувані тип застосовується для даних цілого типу, які можуть приймати обмежений набір значень. Кожному значенню відповідає власне ім'я-ідентифікатор і ціле число, значення цього імені. Оголошення перечислимого типу будується за схемою:

enum ідентифікатор {список перерахування} деклараторів-ініціалізатор;

Тут ідентифікатор задає ім'я перечислимого типу, список перерахування складається з перечіслітелей, розділених комами. Кожен перечіслітель задається ідентифікатором і, можливо, цілим значенням типу char або int, наприклад,

enum color {RED, GREEN, BLUE} en_color;

enum lex_type {CNST, VAR, OPER = 3, FUNC};

Якщо значення перечіслітеля не задано, перший з них отримує значення 0, а кожен наступний - значення, більше на 1. Взагалі будь-який перечіслітель за замовчуванням має значення на 1 більше попереднього. У Сі / Сі + + прийнято записувати ідентифікатори перечіслітелей прописними літерами. Імена перечіслітелей використовується або як іменовані константи або для прісвапіванія змінним перечислимого типу.

У Сі / Сі + + для посилань на змінну того чи іншого типу служать покажчики. Покажчик - це тип даного, значенням якого є адреса іншого даного. При оголошенні покажчика перед ідентифікатором записується знак *. Покажчик може инициализироваться адресою даного, для отримання адреси служить операція & (амперсенд):

double y;

double * px, * py = &y;

Для покажчиків визначені операції порівняння, складання покажчика з цілим числом, віднімання двох покажчиків, а також операція індексування (операція []).

Для звернення до змінної за вказівником виконується операція разименованія, позначена знаком * (зірочка), наприклад, * py = 7.5;.

При оголошенні покажчика може використовуватися описувач const, наприклад,

const int cc = 20;

const int * pc = &cc; / / Можна ініціалізувати адресою константи.

double * const delta = 0.001; / / Покажчик - константа

Крім звичайних змінних і покажчиків в Сі + + є тип "посилання на змінну", що задає для змінної вторинне ім'я (псевдонім). Внутрішнє представлення посилання таке ж, як покажчика, тобто у вигляді адреси змінної, але звернення до змінної за посиланням записується в тій же формі, що і звернення за основним імені. Змінна типу посилання завжди ініціалізується завданням імені змінної, до якої відноситься посилання. При оголошенні заслання за ім'ям типу записується знак & (амперсенд):

int ii;

int & aii = ii;

При цьому описі оператори aii = 5; і ii = 5; еквівалентні.

З змінних будь-якого типу можуть утворюватися масиви. При оголошенні масиву в деклараторів-ініціалізатор за ідентифікатором масиву задається число елементів масиву в квадратних дужках:

int a [5]; / / Масив з п'яти елементів типу int

Індекси елементів масиву завжди починаються з 0, індекс останнього елементу на одиницю менше кількості елементів у масиві. Масив може инициализироваться списком значень у фігурних дужках:

int b [4] = {1, 2, 3, 4};

При наявності списку ініціалізації, що охоплює всі елементи масиву, можна не вказувати число елементів масиву, його буде визначено компілятором:

int c [] = {1, 2, 3}; / / Масив з трьох елементів типу int

Масиви з розмірністю 2 і більше розглядаються як масиви масивів і для кожного вимірювання вказується число елементів:

double aa [2] [2] = {1, 2, 3, 4}; / / Матриця 2 * 2

Масиви типу char можуть инициализироваться строковим літералом. Строковий літерал - це послідовність будь-яких символів, крім лапок і зворотної косої риси, укладена в лапки. Якщо строковий літерал не вміщується на одному рядку, його можна перервати символом "\" і продовжити з початку наступного рядка. У стандарті C + + передбачена й інша можливість запису довгих літералів у вигляді декількох записаних поспіль строкових літералів. Якщо між рядковими літералами немає символів, відмінних від прогалин, такі літерали зливаються компілятором в один.

При розміщенні в пам'яті в кінці строкового літерала додається символ '\ 0', тобто нульовий байт. Строковий літерал може застосовуватися і для ініціалізації покажчика на тип char:

char str1 [11] = "Це рядок",

str2 [] = "Розмір цього масиву визначається"

"Числом знаків в літералі + 1";

char * pstr = "Покажчик з ініціалізацією рядком";

Ім'я масиву в Сі / Сі + + є вказівником-константою, що посилаються, на перший елемент масиву, що має індекс, що дорівнює нулю. Для звернення до елемента масиву вказується ідентифікатор масиву та індекс елемента в круглих дужках, наприклад, c [2], aa [0] [1].

2.3 Структури та об'єднання

Поряд з масивами в Сі / Сі + + є агрегати даних типу структур та об'єднань. Тип структури представляє собою упорядковану сукупність даних різних типів, до якої можна звертатися як до єдиного даному. Опис структурного типу будується за схемою:

struct ідентифікатор

{Деклараторів членів} деклараторы_инициализаторы;

Таке оголошення виконує дві функції, по-перше оголошується структурний тип, по-друге оголошуються змінні цього типу.

Ідентифікатор після ключового слова struct є ім'ям структурного типу. Ім'я типу може бути відсутнім, тоді тип буде безіменний і в інших частинах програми не можна буде оголошувати дані цього типу. Деклараторы_инициализаторы оголошують конкретні змінні структурного типу, тобто дані описаного типу, покажчики на цей тип і масиви даних. Деклараторы_инициализаторы можуть бути відсутні, в цьому випадку оголошення описує тільки тип структури.

Структура, що описує точку на площині, може бути визначена так:

struct Point_struct / / Ім'я структури

{Int x, y;} / / деклараторів членів структури

point1, * ptr_to_point, arpoint [3]; / / Дані структурного типу

Члени (компоненти) структури описуються аналогічно даними відповідного типу і можуть бути скалярними даними, покажчиками, масивами або даними іншого структурного типу. Наприклад, для опису структурного типу "прямокутник зі сторонами, паралельними осям координат" можна запропонувати кілька варіантів:

struct Rect1

{Point p1; / / Координати лівого верхнього кута

Point p2; / / Координати правого нижнього кута

};

struct Rect2

{Point p [2];

};

struct Rect3

{Point p; / / Лівий верхній кут

int width; / / Ширина

int high; / / Висота прямокутника

};

Оскільки при описі членів структури повинні використовуватися тільки раніше певні імена типів, передбачений варіант попереднього оголошення структури, що задає тільки ім'я структурного типу. Наприклад, щоб описати елемент двійкового дерева, що містить покажчики на ліву і праву гілки дерева і покажчик на деяку структуру типу Value, що містить значення даного у вузлі, можна вчинити так:

struct Value;

struct Tree_element

{Value * val;

Tree_element * left, * right;

};

Членами структур можуть бути так звані бітові поля, коли в поле пам'яті змінної цілого типу (int або unsigned int) розміщується кілька цілих даних меншої довжини. Нехай, наприклад, в деякій програмі синтаксичного розбору опис лексеми містить тип лексеми (до шести значень) і порядковий номер лексеми в таблиці відповідного типу (до 2000 значень). Для подання значення типу лексеми достатньо трьох двійкових розрядів (трьох біт), а для подання чисел від 0 до 2000 - 11 двійкових розрядів (11 біт). Опис структури, що містить відомості про лексеме може виглядати так:

struct Lexema

{Unsigned int type_lex: 3;

unsigned int num_lex: 11;

};

Двокрапка з цілим числом після імені члена структури вказує, що це бітове поле, а ціле число задає розмір поля в бітах.

Об'єднання можна визначити як структуру, всі компоненти якої розміщуються в пам'яті з одного і того ж адреси. Таким чином, об'єднання в кожен момент часу містить один з можливих варіантів значень. Для розміщення об'єднання в пам'яті виділяється ділянка, достатній для розміщення члена об'єднання самого великого розміру. Застосування об'єднання також дозволяє звертатися до одного й того ж полю пам'яті за різними іменами й інтерпретувати як значення різних типів.

Опис об'єднання будується за тією ж схемою, що й опис структури, але замість ключового слова struct використовується слово union, наприклад, об'єднання uword дозволяє інтерпретувати поле пам'яті або як unsigned int, або як масив з двох елементів типу unsigned char.

union uword

{Unsigned int u;

unsigned char b [2];

};

Описи типів, які декларуються програмістом, в тому числі структур та об'єднань можуть бути досить великими, тому в Сі / Сі + + передбачена можливість присвоювання типам власних імен (синонімів), досягаючи при цьому підвищення наочності програмних текстів. Синонім імені типу вводиться з ключовим словом typedef і будується як звичайне оголошення, але ідентифікатори в деклараторів в цьому випадку інтерпретуються як синоніми описаних типів. Синоніми імен типів прийнято записувати прописними літерами, щоб відрізняти їх від ідентифікаторів змінних. Нижче наведено кілька прикладів оголошення синонімів імен типів.

typedef struct {double re, im} COMPLEX;

typedef int * PINT;

Після таких оголошень синонім імені може використовуватися як специфікатор типу:

COMPLEX ca, * pca; / / змінна типу COMPLEX і покажчик на COMPLEX

PINT pi; / / покажчик на int

Наведене вище опис структур і об'єднань в основному відповідає їх побудови у мові Сі. У Сі + + структури та об'єднання є окремими випадками об'єктних типів даних. Додаткові відомості про це будуть наведені при розгляді об'єктно-орієнтованих засобів Сі + +.

2.4 Операції та вирази

Незважаючи на обмежений набір базових типів даних (цілі і речові арифметичні дані і рядкові літерали) в мові Сі + + визначено великий набір операцій над даними, частина з яких безпосередньо відповідає машинним командам. Як і у всіх мовах програмування, операції служать для побудови виразів. Вираз є послідовність операндів і знаків операцій і служить для обчислення деякого значення.

Як операнди у виразі виступають ідентифікатори змінних, константи, і рядкові літери, які є первинними виразами. Вираз, укладену в круглі дужки, також розглядається як первинне. Кожна операція передбачає використання певних типів операндів (цілих, дійсних, покажчиків). Операція присвоювання в Сі + + також є виразом, у зв'язку з цим розрізняються операнди, яким можна привласнити нового значення і операнди, значення яких не може змінюватися. Щоб операнду можна було привласнити значення, йому повинна відповідати область пам'яті і компілятору повинен бути відомий адресу цієї пам'яті. Такі операнди називають L-виразами (від англійського left-лівий), так як вони можуть бути записані в лівій частині оператора присвоювання.

Результат обчислення виразу залежить від пріоритетів операцій. У Сі + + складна система пріоритетів операцій, що включає 16 рівнів. У таблиці 2.1 наведено перелік операцій Сі + + з зазначенням їх пріоритетів, призначення і схеми запису.

Таблиця 2.1

Пріоритет

Знак операції

Призначення

Схема

1

::

Доступ до глобального імені або імені з іншої області

:: Ідентифікатор (глобальний)
ім'я області:: імя_члена_структури

1

->

Звернення до члена структури за вказівником на структуру

покажчик -> імя_члена_структури

1

.

Звернення до члена структури на ім'я структури

імя_структури. імя_члена_структури

1

[]

Звернення до елементу масиву

покажчик [індекс]

1

()

Перетворення типу даного

імя_тіпа (вираз) або (тип) вираз

1

()

Виклик функції

функція (аргументи)

2

+ +

Автоувеліченіе

+ + L-значення або
L-значення + +

2

-

Автоуменьшеніе

- L-значення або
L-значення -

2

~

Бітове інвертування

~ Целое_вираженіе

2

!

Логічне заперечення

! вираз

2

-

Одномісний мінус

- Вираз

2

+

Одномісний плюс

+ Вираз

2

&

Отримання адреси

& L-значення

2

*

Разименованія покажчика

* Покажчик

2

new

Виділення динамічної пам'яті

new тип даного

2

delete

Звільнення пам'яті

delete покажчик

2

delete []

Звільнення пам'яті для масиву

delete [] покажчик

2

sizeof

Розмір даного

sizeof вираз

2

Розмір типу даного

sizeof (ім'я типу)

3

*

Множення

вираз * вираз

3

/

Розподіл

вираз / вираз

3

%

Залишок від ділення без остачі

вираз% вираз

4

-> *

Звернення до члена структури за вказівником

указатель_на_структуру -> * імя_члена_структури-покажчика

4

.*

Звернення до члена структури на ім'я структури

імя_структури .*
імя_члена_структури-покажчика

5

+

Додавання

вираз + вираз

5

-

Віднімання

вираз - вираз

6

<<

Зрушення вліво

целое_вираженіе <<целое_вираженіе

6

>>

Зсув вправо

целое_вираженіе>> целое_вираженіе

7

<

Менше

вираз <вираз

7

<=

Менше або дорівнює

вираз <= вираз

7

>

Більше

вираз> вираз

7

> =

Більше або дорівнює

вираз> = вираз

8

==

Так само

вираз == вираз

8

! =

Не дорівнює

вираз! = вираз

9

&

Поразрядно кон'юнкція

вираз & вираз

10

^

Заперечення рівнозначності

вираз ^ вираз

11

|

Поразрядно диз'юнкція

вираз | вираження

12

& &

Логічне "І"

вираз & & вираз

13

| |

Логічне "АБО"

вираз | | вираз

14

? :

Умовний вираз

вислів? вираз1: вираз2

15

=

Просте присвоювання

вираз = вираз

15

@ =

Складений присвоювання, знак @ - один зі знаків операцій * /% + - <<>> & ^ |

вираз @ = вираз

16

,

Операція слідування

вираз, вираз

Розглянемо особливості застосування основних з перерахованих вище операцій.

Операція "::" (дві двокрапки) застосовується для уточнення імені об'єкта програми у разі, коли в цьому місці програми відомі два однаковому імені, наприклад, коли одне ім'я оголошено глобально, а інше в тілі функції. Якщо імені передують дві двокрапки, то це глобальне ім'я.

Для звернення до членів структури або об'єднання можна скористатися або ім'ям структурного даного, або покажчиком на структурний дане. У першому випадку повне ім'я члена структури складається з імені самої структури та імені члена структури, розділених крапкою. У другому випадку за ім'ям покажчика на структуру ставиться знак -> (стрілка), а за ним ім'я члена структури. Нехай у програмі оголошений структурний тип AnyStruct, що містить компонент з ім'ям member типу int і оголошені

AnyStruct s1; / / Дане s1 типу AnyStruct

AnyStruct * ps1 = &s1; / / Покажчик на даний типу AnyStruct

Тоді до члена структури member з s1 можна звернутися як до s1.member або як ps1-> member.

Оскільки членом структури може бути покажчик, в Сі + + є спеціальні операції разименованія такого покажчика, операції .* і ->. Нехай одним з членів структури AnyStruct є покажчик pp1 на даний типу int. Тоді вираження s1 .* pp1 і ps1-> * pp1 забезпечать доступ до значення даного, на яке вказує pp1 з s1.

Вище зазначалося, що ім'я масиву в Сі / Сі + + інтерпретується як покажчик-константа на перший елемент масиву. Для разименованія покажчика, тобто для доступу до даного за вказівником на це дане служить операція * (зірочка). Отже, якщо в програмі оголошений масив

int Array1 [10];

то вираз * Array1 = 0 служить для присвоєння нульового значення першого елемента масиву. Щоб звернутися до довільної елементу масиву, потрібно вказати індекс елемента, наприклад, Array1 [3]. Це вираз еквівалентно висловом * (Array1 + 3), тобто потрібно спочатку збільшити покажчик Array1 на 3 одиниці, а потім разименовать отриманий покажчик. При складанні покажчика на об'єкт деякого типу T з цілим числом N значення покажчика збільшується на N, помножене на довжину даного типу T. Відзначимо, що індекс можна задавати не тільки для імен масивів, але і для будь-якого типу покажчика, крім покажчика на тип void:

int * pint = & Array [4]; pint [2] = 1;

У цьому прикладі покажчик pint инициализирован адресою п'ятого елементу масиву Array, а потім сьомого елементу цього масиву присвоєно значення 1.

В якості індексу може задаватися будь-який вираз із значенням цілого типу.

Оскільки Сі + + є типізовані мовою, у ньому визначені явні і неявні перетворення типів даних. Неявні перетворення виконуються при двомісних арифметичних операціях та операції привласнення і називаються стандартними арифметичними перетвореннями. Ці перетворення виконуються в наступній послідовності:

- Якщо один операнд має тип long double, інший операнд перетворюється в тип long double;

- Інакше, якщо один операнд має тип double, інший операнд перетворюється в тип double;

- Інакше, якщо один операнд має тип float, інший операнд перетворюється в тип float;

- Інакше, якщо один операнд має тип unsigned long int, інший операнд перетворюється в тип unsigned long int;

- Інакше, якщо один операнд має тип long int,> інший операнд перетворюється в тип long int;

- Інакше, виконуються стандартні перетворення для цілих, при цьому типи char, short int і бітові поля типу int перетворюються на тип int, потім, якщо один операнд має більший розмір (більший діапазон значень), ніж інший операнд, то другий операнд перетвориться до типу операнда більшого розміру;

- В інших випадках операнди мають тип int.

Явне перетворення типів може бути задане в двох формах. Перша форма сумісна з Сі, в ній за ім'ям типу в круглих дужках записується конвертувати значення, яке може бути первинним виразом чи виразом з одномісною операцією. Ім'я типу в цьому випадку може бути представлено послідовністю описувачів, наприклад, (long int *) pp определеяются перетворення деякого даного pp в тип покажчика на long int. Друга форма перетворення типу записується як виклик функції, при цьому ім'я типу має задаватися ідентифікатором, наприклад, int (x). Слід зазначити, що результат явного перетворення не є L-значенням.

Операції автоувеліченія і автоуменьшенія (+ + і -) можуть бути префіксним і постфіксні і викликають збільшення (зменшення) свого операнда на одиницю, тобто вираз + + x еквівалентно x = x +1, а - x еквівалентно x = x - 1. Префіксная операція виконується до того, як її операнд буде використаний в обчисленні виразу, а постфіксній операція виконується після того, як її операнд буде використаний у виразі, наприклад, в результаті обчислення виразу

+ + X * 2 + y - * 3

мінлива x спочатку збільшується на 1, а потім збільшується на 2, мінлива y спочатку множиться на 3, потім зменшується на 1. Якщо перед обчисленням цього виразу x і y були рівні 1, то результат виразу буде дорівнює 5, крім того мінлива x отримає значення 2, а змінна y - значення 0. Таким чином, операції автоувеліченія і автоуменьшенія завжди дають побічний ефект, змінюють значення своїх операндів. Операнди цих операцій повинні бути L-значеннями.

Операція ~ (тильда) застосовується тільки до цілого значенням і замінює всі біти свого операнда зі значенням 0 на 1, а біти зі значенням 1 на 0.

Логічне заперечення (операція!) Повертає значення 0 цілого типу, якщо операнд не дорівнює нулю, або значення 1, якщо операнд дорівнює нулю.

Операції "одномісний +" і "одномісний -" мають звичайний математичний сенс, знак + не змінює значення операнда, знак - змінює знак операнда на протилежний.

Для отримання адреси операнда, що є L-значенням, застосовується операція & (амперсанд). Результатом цієї операції буде покажчик на відповідний тип даного. Разименованія покажчика, тобто отримання значення даного за вказівником на нього забезпечується операцією * (зірочка). Результат операції разименованія є L-значенням.

У Сі + + визначено операції розміщення даних у динамічній пам'яті та видалення динамічних даних з пам'яті.

Операція new вимагає як операнд імені типу і призначена для розміщення даного зазначеного типу у динамічній пам'яті, результатом операції буде покажчик на дане. При неможливості виділити пам'ять операція new повертає значення NULL - зумовлену константу, що має нульове значення практично у всіх компіляторах Сі і Сі + +. Пам'ять, що виділяється операцією new, можна ініціалізувати, вказавши за ім'ям типу скалярного даного початкове значення в круглих дужках, завдання початкових значень для агрегатів даних буде розглянуто пізніше. Приклади застосування операції new:

int * ip = new int / * створення об'єкта типу int і отримання покажчика на нього * /

int * ip2 = new int (2); / / те саме з установкою початкового значення 2

inr * intArray = new int [10]; / / масив з 10 елементів типу int

double ** matr = new double [m] [n]; / / матриця з m рядків і n стовпців

Дане, розміщене у динамічній пам'яті операцією new, видаляється з пам'яті операцією delete з операндом-дороговказом, значення якого отримано операцією new, наприклад,

delete intArray; delete ip2;

Операція delete тільки звільняє динамічну пам'ять, але не змінює значення покажчика-операнда. Програміст повинен пам'ятати, що після звільнення пам'яті використовувати цей покажчик для звернення до даного не можна.

Розмір даного чи типу даного в байтах можна отримати за операції sizeof. Операнд може бути будь-якого типу, крім типу функції і бітового поля. Якщо операндом є ім'я типу, воно повинно полягати в дужки. Значення, що повертається має зумовлений тип size_t, це цілий тип, розмір якого визначається реалізацією компілятора, зазвичай типу size_t відповідає unsigned int. Розмір масиву дорівнює числу байт, займаних масивом в пам'яті, розмір строкового літерала - це число знаків у літералі +1, тобто завершальний нульовий байт враховується при визначенні довжини літерала. Значення, що повертається sizeof є константою.

Двомісні арифметичні операції множення (*), ділення (/), отримання залишку від ділення без остачі (%), складання (+) і віднімання (-) мають звичайний сенс і звичайний відносний пріоритет. Якщо операнди арифметичної операції мають різні типи, попередньо виконуються стандартні арифметичні перетворення і тип результату операції визначається загальним типом операндів після стандартних перетворень. Отже, вираження 7 / 2 буде мати значення 3 типу int, так як обидва опернда мають тип int, а вираз 7.0 / 2 дасть результат 3.5 типу double, оскільки цей тип має перший операнд.

Операції відносини двох виразів (<, <=,>,> =) вимагають операндів арифметичного типу або ж обидва операнда повинні бути покажчиками на однаковий тип. У разі операндів арифметичного типу обчислюються значення операндів, виконуються стандартні арифметичні перетворення і повертається 1 типу int, якщо відношення виконується (істинно), або 0, якщо ставлення не виконується (неправильно). Коли порівнюються два покажчика, результат залежить від відносного розміщення в пам'яті об'єктів, на які посилаються покажчики. Операції порівняння (== і! =) Виконуються аналогічним чином, але мають менший пріоритет.

Вирази відносин можуть з'єднуватися логічними зв'язками & & (кон'юнкція, логічне множення) і | | (диз'юнкція, логічне додавання). У загальному випадку операндами логічних зв'язок можуть бути будь-які скалярні значення і операція & & дає реззультат, рівний 1 типу int, якщо обидва операнди мають нульові значення, а операція | | дає результат, рівний 0, якщо значення обох операндів нульові. Застосовується скорочена форма обчислення значення логічних зв'язок, якщо в операції & & перший операнд дорівнює нулю, то другий операнд не обчислюється і повертається 0, якщо в операції | | перший операнд не дорівнює нулю, то другий операнд не обчислюється і повертається значення 1.

Як вже зазначалося, присвоювання, що позначається знаком = в Сі / Сі + + розглядається як операція і повертає значення, яке було присвоєно лівому операнд. Операція присвоювання обчислюється справа наліво, тобто спочатку обчислюється присваиваемое значення, потім виконується присвоювання. Це дозволяє записувати вирази виду x = y = z = 1 для установки однакових значень декільком змінним. Можна, хоча це і знижує наочність програми, будувати і вирази з побічним ефектом виду (x = 2) * (y = 3) + (z = 4). Результатом цього виразу буде 24, але одночасно змінні x, y і z отримають нові значення.

Крім простого привласнення є набір складових операцій привласнення, в яких присвоювання поєднується із зазначеною двомісній операцією. Запис x + = y еквівалентна висловом x = x + y.

Для цілих операндів визначені операції зсуву вліво і вправо. При виконанні операції e1 <<e2 біти першого операнда зсуваються вліво на e1 розрядів і результат має тип першого операнда. Звільнитися праві розряди заповнюються нулями. При зсуві вправо (e1>> e2) якщо e1 має тип unsigned, що звільняються ліві розряди заповнюються нулями, а при e1 типу signed в вивільнюваних лівих розрядах повторюється знаковий розряд.

Над цілими операндами припустимі операції порозрядного логічного множення, логічного додавання і виключає або (заперечення рівнозначності). У цих операціях операнди розглядаються як послідовності бітів і операція виконується над кожною парою відповідних розрядів з обох операндів. Наприклад, результатом виразу (x>> (p - n +1)) & (~ (~ 0 <<n)) буде виділення з цілого беззнакового xn бітів, починаючи з біта з номером p, і зсув виділених бітів вправо, т. е. виділення n-розрядного цілого, що зберігається в машинному слові x починаючи з p-го розряду.

У Сі / Сі + + є конструкція, яка називається умовним виразом. Умовний вираз будується за схемою:

умова? вираз1: вираз2

В якості умови може виступати будь-скалярний вираз. Якщо результат обчислення умови ненульовий, то значенням всього виразу буде вираз1, при нульовому значенні умови значення всього виразу визначається вираженіем2. Другий і третій операнди умовного вираження повинні бути або обидва арифметичного типу, або однотипними структурами або об'єднаннями, або покажчиками однакового типу, або один з них - покажчик на який-небудь тип, а інший операнд NULL або має тип void *. Вираз x> 0? 1: 0 повертає 1, якщо x більше 0, і 0 у противному випадку.

Вираз може бути представлено послідовністю виразів, розділених комами, в цьому випадку обчислюються всі вирази зліва направо і повертається значення останнього виразу в списку. Наприклад в результаті обчислення виразу

x = 2, e * 3, x +1 буде отримано значення 3 і попутно x отримає значення 2. Результат множення e * 3 ніяк не може бути використаний.

2.5 Оператори Сі + +

Оператори - це синтаксичні конструкції, що визначають дії, що виконуються програмою. У Сі / Сі + + є наступні типи операторів: оператори-вирази, оператори вибору, оператори циклу і оператор переходу. Синтаксис деяких операторів містить вирази, які відіграють роль умов, в завісісмості від виконання або невиконання яких вибирається та або інша послідовність дій. Оскільки в Сі немає булевих виразів, в якості умов використовуються будь-які висловлювання, що дають скалярні значення, і умова вважається виконаним, якщо це значення відмінне від нуля, і невиконаним, якщо воно дорівнює нулю. Декілька операторів можуть бути об'єднані в складений оператор висновком їх у фігурні (операторні) дужки. Ознакою кінця оператора (крім складеного оператора) служить крапка з комою, що є в цьому випадку частиною оператора.

Перед будь-яким оператором може бути записана мітка у вигляді ідентифікатора, відокремленого від позначуваних оператора двокрапкою. Мітка служить тільки для позначення її в операторі переходу.

Найбільш простим є оператор-вираз, що представляє собою повне вираження, закнчівающееся крапкою з комою, наприклад,

x = 3; y = (x +1) * t; i + +;

Вираз, оформлене як оператор, обчислюється, але його значення втрачається і дію оператора-вирази полягає в побічні ефекти, що супроводжують обчислення, наприклад, при виконанні операцій привласнення, автоувеліченія і автоуменьшенія.

Оператори вибору в Сі / Сі + + представлені умовним оператором і перемикачем. Умовний оператор аналогічний умовним операторам інших мов програмування і може використовуватися в скороченій та повної формах, яким відповідають схеми:

if (вираз-умова) оператор

if (вираз-умова) оператор-1 else оператор-2

У скороченій формі умовного оператора обчислюється вираз-умова і, якщо його значення відмінне від нуля, виконується наступний за умовою оператор, у противному випадку не проводиться жодних дій.

У повній формі умовного оператора при ненульове значення виразу-умови виконується оператор-1 з наступним переходом до наступного оператору програми, а при нульовому значенні виразу умови виконується оператор-2 з переходом до наступного оператору програми.

Перемикач дозволяє вибрати одну з декількох можливих гілок висісленій і будується за схемою:

switch (цілий вираз) оператор.

Оператор в цьому випадку представляє собою тіло перемикача, практично завжди є складовим і має такий вигляд:

{Case константа-1: оператори

case константа-2: оператори

default: оператори

};

Виконання перемикача полягає в обчисленні керуючого вираження і перехід до групи операторів, помічених case-міткою, рівної керуючому висловом, якщо такий case-мітки немає, виконуються оператори за міткою default. Пункт default може бути відсутнім і тоді, якщо керуючому вираженню не відповідають жодна case-мітка, весь перемикач еквівалентний порожньому оператору. Слід враховувати, що при виконанні перемикача відбувається перехід на оператора до обраної case-міткою і далі оператори виконуються в природному порядку. Наприклад, в перемикачі

switch (count)

{Case 1: x = 1;

case 2: x = 2;

case 3: x = 3;

default: x = 4;

};

якщо значення count дорівнює 1, то після переходу на case 1 будуть виконані всі оператори, у результаті x стане рівним 4. Щоб розділити гілки перемикача, в кінці кожної гілки треба записати оператор break, що не має операндів. З цього оператора відбувається вихід з перемикача до наступного оператору програми:

switch (count)

{Case 1: x = 1; break;

case 2: x = 2; break;

case 3: x = 3; break;

default: x = 4;

};

Тепер залежно від значення count буде виконуватися тільки одна гілка перемикача і x буде приймати одне з чотирьох передбачених значень.

У Сі / Сі + + є три варіанти оператора циклу: цикл з передумовою, цикл з постусловіем і цикл з параметром.

Цикл з передумовою будується за схемою

while (вираз-умова) оператор.

При кожному повторенні циклу обчислюється вираз-умова і якщо значення цього виразу не дорівнює нулю, виконується оператор - тіло циклу. Цикл з постусловіем будується за схемою:

do оператор while (вираз-умова).

Вираз-умова обчислюється і перевіряється після кожного повторення оператора - тіла циклу, цикл повторюється, поки умова виконується. Тіло циклу у циклі з постусловіем виконується хоча б один раз.

Цикл з параметром будується за схемою:

for (E1; E2; E3) оператор

де E1, E2 і E3 - вирази скалярного типу. Цикл з параметром реалізується за наступним алгоритмом:

1. Обчислюється вираз E1. Зазвичай це вираз виконує підготовку до початку циклу.

2. Обчислюється вираз E2 і якщо воно дорівнює нулю виконується перехід до наступного оператору програми (вихід з циклу). Якщо E2 не дорівнює нулю, виконується крок 3.

3. Виконується оператор - тіло циклу.

4. Обчислюється вираз E3 - виконується підготовка до повторення циклу, після чого знову виконується крок 2.

Нехай потрібно підрахувати суму елементів деякого масиву з n елементів.

З використанням циклу з передумовою це можна зробити так:

int s = 0;

int i = 0;

while (i <n) s + = a [i + +];

Це ж завдання із застосуванням циклу з постусловіем вирішується наступними операторами:

int s = 0;

int i = 0;

do s + = a [i + +]; while (i <n);

Оскільки в даному випадку повтореннями циклу управляє параметр i, це завдання можна вирішити й за допомогою циклу третього типу:

int i, s;

for (s = 0, i = 0; i <n; i + +) s + = a [i];

Оголошення змінних можна внести у вираз E1 оператора for і все записати у вигляді одного оператора for з порожнім тілом циклу:

for (int i = 0, s = 0; i <n; s + = a [i + +]);

Для переривання повторень оператора циклу будь-якого типу в тілі циклу може бути використаний оператор break. Для переходу до наступного повторення циклу з будь-якого місця тіла циклу може бути застосований оператор continue. Ці оператори за своїм назаначенію аналогічні відповідним операторам мови Паскаль.

Незважаючи на те, що Сі + + містить повний набір операторів для структурного програмування, у нього все ж включений оператор переходу:

goto мітка;

Мітка задає адресу переходу і повинна позначати оператор в тому ж складеному операторі, якому належить оператор goto. Вхід у складений оператор, що містить оголошення даних, не через його початок, заборонений.

2.6 Функції

Будь-яка програма на Сі / Сі + + містить хоча б одну функцію. Алгоритм вирішення будь-якої задачі реалізується шляхом викликів функцій. Одна з функцій вважається головною і має фіксоване ім'я, ця функція викликається операційною системою при запуску програми, а з неї можуть викликатися інші функції. При роботі в MS DOS головна функція має ім'я main.

Опис функції має загальний синтаксис зовнішнього визначення і відрізняється від опису змінного синтаксисом деклараторів-ініціалізатор, який містить ідентифікатор (ім'я) функції і список параметрів у круглих дужках. Тип функції - це тип значення, що повертається функцією. Функція може повертати значення будь-якого типу, крім масиву і функції, але допускається повертати покажчик на масив або покажчик на функцію. Якщо функція не повертає ніякого значення, тип функції позначається ключовим словом void.

У списку параметрів вказуються типи і імена параметрів, що передаються у функцію при її виклику. Якщо функція не вимагає передачі їй аргументів, список параметрів може бути порожнім. Для сумісності з програмами, написаними на Сі, рекомендується як порожнього списку параметрів вказувати ключове слово void.

Повний опис функції містить також тіло функції, що представляє собою складений оператор. Складовою оператор - це послідовність описів внутрішніх даних і операторів, укладена у фігурні дужки. Слід зазначити, що в Сі / Сі + + опис функцій не може бути вкладеним, в тілі функції не можна оголосити іншу функцію.

Функція, що повертає середнє арифметичне трьох речових даних, може бути описана так:

double sred (double x, double y, double z)

{Double s;

s = x + y + z;

return s / 3;

};

Для виклику такої функції за умови, що попередньо оголошені змінні p, a, b, c і, можна записати оператор:

p = sred (a, b, c);

Очевидно, що функція повинна бути описана до того, як зустрінеться її перший виклик. Це не завжди можливо, наприклад, коли дві функції викликають один одного. Коли програма складається з декількох файлів, повний опис функції повинно бути присутніми тільки в одному файлі, але виклики функції можливі з різних файлів програми. Щоб дозволити подібні протиріччя передбачені дві форми опису функцій, повна форма, звана також визначенням функції, і скорочена, звана описом прототипу функції або просто прототипом. Прототип функції містить тільки заголовок функції і задає таким чином ім'я функції, тип значення і типи параметрів. За цією інформацією при компіляції програмного файлу можна перевірити правильність запису виклику функції та використання, що повертається. У Сі + + потрібно, щоб викликом будь-якої функції передувало в тому ж файлі або повне визначення функції або опис її прототипу.

Слід відзначити ряд додаткових можливостей опису функцій в Сі + +:

- При описі прототипу функції можна не вказувати імена параметрів, досить вказати їх типи.

- Для частини параметрів функції можна задавати значення за замовчуванням, що дозволяє викликати функцію з меншим числом аргументів, ніж передбачено описом функції. Значення за замовчуванням можна вказувати тільки для останніх параметрів у списку. Наприклад, функція sred могла б бути описана так:

double sred (double x, double y, double z = 0)

{Double s;

s = x + y + z;

return s / 3;

};

До такої функції можна звертатися з двома і з трьома аргументами.

- Функція може мати змінне число параметрів, для частини параметрів можуть бути невідомі їх типи. Невідома частину списку параметрів позначається трьома крапками. Наприклад, функція з прототипом

int varfunc (int n, ...);

має один обов'язковий параметр типу int і невизначене число параметрів невідомих типів. Правильна інтерпретація такого списку параметрів у тілі функції вимагає від програміста додаткових зусиль.

При виконанні функції аргументи передаються у функцію за значеннями. Це означає, що для кожного аргументу в пам'яті створюється його копія, яка і використовується при обчисленні функції. Отже, будь-які зміни значень аргументів на тілі функції губляться при виході з функції. Якщо аргумент є масивом, у функцію передається покажчик на початковий елемент масиву і присвоювання елементів масиву в тілі функції змінюють значення елементів масиву аргументу.

У С + + з оголошенням функції пов'язується так звана сигнатура функції, обумовлена ​​типом значення, що повертається і типами параметрів. Це дозволяє призначати функцій, що мають аналогічне призначення, але використовують параметри різних типів, однакові імена. Наприклад, поряд з наведеної вище функцією sred для вущественних аргументів, в тій же програмі може бути функція

double sred (int x, int y, int z = 0)

{Int s;

s = x + y + z;

return s / 3;

};

Функція, у тілі якої відсутні оператори циклу і перемикачі, може бути оголошена з додатковим описувачем inline. У точці виклику такої функції при компіляції просто вставляється тіло функції з відповідною заміною параметром на аргументи виклику. У результаті економиться час на передачу параметрів, перехід на підпрограму та організацію повернення в зухвалу програму. Функції з описувачем inline називають вбудованими, вони реалізуються як відкриті продпрограмми. Типовий приклад такої функції - визначення найбільшого (найменшого) з двох чисел:

inline int max (int x1, int x2)

{Return x1> x2? x1: x2}

У системах програмування Turbo C + + і Borland C + + головна функція (функція main) може приймати три параметри, що дозволяє викликати Сі-програми з командного рядка DOS з передачею в програму необхідних аргументів. Стандартний прототип функції main має вигляд:

int main (int argc, char * argv [], char * envp [])

У конкретній програмі можна оголошувати функцію main без значення, що повертається (повертає тип void), з використанням тільки двох перших параметрів або взагалі без параметрів. Параметри argc і argv служать для передачі в програму аргументів у вигляді масиву рядків, argc містить число елементів цього масиву, а argv - це масив покажчиків на елементи масиву, причому перший елемент масиву, на який вказує argv [0], містить ім'я програми (ім'я exe-файлу програми), інші елементи являють собою аргументи з командного рядка DOS. Параметр envp використовується для доступу до елементів поточного середовища DOS.

2.7 Бібліотека часу виконання

У визначенні мов Сі і Сі + + відсутні оператори введення-виведення, операції над рядковими даними і багато інших засобів, які є в інших мовах програмування. Цей недолік компенсується додаванням в системи програмування Сі та Сі + + бібліотек функцій, що підключаються до робочих програмах при редагуванні зв'язків і званих бібліотеками часу виконання. Відділення цих бібліотек від компілятора дозволяє в необхідних випадках використовувати різні варіанти цих бібліотек, наприклад, для різних моделей ЕОМ або операційних систем.

Частина функцій з бібліотек часу виконання стандартизована, в стандарті зафіксовані ім'я функції, її призначення, перелік і типи параметрів і тип значення, що повертається. Інші функції орієнтовані на конкретні моделі ЕОМ та операційні системи, способи їх виклику та використання можуть бути різними в системах програмування різних фірм.

Щоб забезпечити можливість контролю правильності виконання функцій при компіляції програми, в систему програмування входять файли заголовків функцій часу виконання. У файлах заголовків визначаються прототипи бібліотечних функцій, а так само константи та типи даних, що використовуються цими функціями.

Нижче наведено імена деяких файлів заголовків і призначення описаних в них прототипів груп функцій.

Стандартом Сі визначені наступні файли заголовків:

ASSERT.H - Містить макроси для повідомлень про помилки при виконанні умови, що задається програмістом.

CTYPE.H - Функції для перевірки та перетворення даних типу char.

FLOAT.H - Макроси для операцій над числами з плаваючою крапкою.

LIMITS.H - Макроси, що задають діапазони подання цілих.

LOCALE.H - Представлення дати, часу, грошових одиниць.

MATH.H - Пакет стандартних математичних функцій.

SETJUMP.H - Імена типів і функції для реалізації операторів переходу, використовується рідко.

SIGNAL.H - Макроси для сигналізації про помилки згідно стандарту ANSI.

STDARG.H - Макроси для виконання функцій із змінним числом аргументів.

STDDEF.H - Визначення загальних типів для покажчиків, типів size_t і NULL.

STDIO.H - Стандартні функції введення-виведення.

STDLIB.H - Визначення загальних типів, змінних і функцій.

STRING.H - Функції для операцій над рядковими даними.

TIME.H - Структури і функції для операцій з датами і часом.

У Сі + + додані операції з комплексними числами і десятковими даними:

BCD.H - Дані, представлені в десятковій системі числення

COMPLEX.H - Функції та операції над комплексними числами.

Є також файли прототипів функцій для розподілу та звільнення динамічної пам'яті, використання коштів DOS і BIOS.

Щоб використовувати будь-які функції з бібліотек часу виконання у програму повинен бути включений файл-заголовок з прототипами необхідних функцій. Включення в програму файлу-заголовка забезпечується препроцесорну директивою

# Include <назва файлу>

Наприклад, для включення заголовка з функціями введення-виведення в стилі Сі на початку програми записується рядок

<PRE>

Очевидно, будь-яка програма використовує будь-які вхідні дані і куди-небудь виводить отримані результати, тому файл заголовків stdio.h присутні майже в усіх програмах. Відзначимо деякі фугкціі з цього файлу. Опції введення-виведення використовують поняття потоку, що розглядається як послідовність байтів. Потік може бути пов'язаний з дисковим файлом або іншим пристроєм введення-виведення, в тому числі з консоллю, коли введення здійснюється з клавіатури, а висновок - на екран монітора. Передбачені кілька стандартних потоків:

stdin - стандартне введення,

stdout - стандартний вивід,

stderr - для виведення повідомлень про помилки,

stdprn - стандартний пристрій друку,

stdaux - стандартний послідовний порт.

Потоки stdin, stdout і stderr звичайно зв'язуються з консоллю, але можуть бути змінені на інші пристрої. Призначення двох останніх потоків залежить від використовуваної апаратури. Стандартні потоки автоматично відкриваються при запуску Сі-програми і закриваються при її завершенні. Потоки, що створюються програмістом, відкриваються функцією fopen і закриваються функцією fclose.

Опції введення-виведення з stdio.h умовно можна розбити на чотири групи: введення-виведення байтів, введення-виведення рядків, форматний введення-виведення і так званий прямий (бесформатний) введення-виведення. Тут відзначимо лише окремих представників перших трьох груп, призначених для введення з потоку stdin і виведення в потік stdout.

Функція int getchar () служить для введення одного символу з клавіатури і повертає код символу, перетворений до типу int. Функція int putchar (int c) виводить символ c в чергову позицію на екрані монітора.

Для введення рядка з клавіатури служить функци char * gets (char * buf), яка читає ввід з клавіатури (до символу нового рядка або натискання клавіші Enter) і поміщає коди прочитаних символів у буфер, адреса якого задається параметром buf, в кінці рядка додається нульової байт. Висновок рядка виконує функціонально int puts (char * string), яка виводить рядок за адресою string на екран, поки в рядку не зустрінеться нульовий байт і повертає код останнього виведеного символу.

Функції форматного введення-виведення приймають в якості параметрів рядок з описом формату представлення даних на зовнішньому пристрої, і список запровадження або виведених даних. Рядок опису формату складається зі звичайних символів, керуючих символів типу нового рядка, повернення каретки і т.п. і специфікацій полів введення або виведення. Кожна така специфікація починається знаком% (відсоток) за яким йдуть коди прапорів, розмір поля введення або виведення, число цифр у дробовій частині числа, префікса розміру цього та коду типу формату. Обов'язково вказувати тільки тип формату, інші компоненти специфікації формату задаються за потребою. Відзначимо деякі коди типу формату:

d - для представлення цілого із знаком в десятковій системі числення,

i - для представлення цілого без знака в десятковій системі числення,

f - для представлення числа з плаваючою крапкою в природній формі,

e або E - представлення числа з плаваючою крапкою в експоненційної формі,

s - введення-виведення строкових даних,

c - введення-виведення символів.

Для форматного висновку слугує функція int printf (char * format, ...), що має список параметрів змінної довжини, кількість додаткових параметрів повинно відповідати числу специфікацій формату в форматної рядку, дані для виведення можуть задаватися виразами, відповідальність за правильне завдання специфікації формату для кожного виведеного даного повністю лежить на програмістові. Приклад застосування функції printf:

printf ("\ nx =% d, y =% f% s", x, y, st);

При виконанні цієї функції проглядається рядок формату зліва направо і символи не є специфікаціями формату копіюються на вивідний пристрій, керуючий символ \ n переводить курсор на екрані до початку наступного рядка, коли зустрічається специфікація формату, вибирається черговий аргумент з списку висновку, перетвориться в відповідно до форматом і виводиться в чергову позицію екрана. Для правильного висновку потрібно, щоб змінна x була типу int, y - типу float, а змінна st - типу char *.

Форматний введення виконує функція int scanf (char * format, ...) в якій список введення повинен задавати покажчики на вводяться змінні. Якщо в рядку формату присутні символи, що не входять в специфікації форматів і не є пробільними, то у вхідному потоці повинні бути присутніми ті ж символи. Пробільними символами вважаються знаки пробілу, табуляції, нового рядка, вони зчитуються з потоку вводу, але не беруть участі у формуванні вхідних даних. Коли в форматної рядку зустрічається специфікація формату, у вхідному потоці проаускаются пробільні символи, а наступні символи інтерпретуються відповідно до типу формату, перетворюються у внутрішнє представлення і отримане значення записується в пам'ять за адресою чергового елемента списку введення. Наприклад, для введення двох змінних цілого типу та однієї дійсного типу можна застосувати оператора виклику функції

scanf ("% d% d% f", & x1, x2 &, & y);

Тут x1 і x2 повинні бути типу int, а y - типу float. У вхідному потоці вводяться значення повинні розділятися хоча б одним пропуском.

Більш повну інформацію про функції введення-виведення в стилі Сі можна отримати в довідковій системі інтегрованого середовища Borland C + +.

Недоліком розглянутих вище функцій вводу-виводу є відсутність контролю відповідності типів форматів і типів вводяться (виведених) даних, часто приводить до помилок в програмах. У Сі + + включені власні кошти потокового введення-виведення, що забезпечують жорсткий контроль типів в операціях введення-виведення. Для цього визначено чотири нових стандартних потоку:

cin - для введення даних,

cout - для виведення даних,

cerr - виведення повідомлень про помилки без буферизації виведення,

clog - виведення повідомлень про помилки з буферизацією виводу.

В якості знака операції виведення визначені знаки <<, а знаком операції введення - знаки>>, ті ж, що і для операцій зсуву. Компілятор на контекст визначає, яку операцію задають ці символи, введення-виведення або зсув.

Щоб використовувати засоби введення-виведення Сі + + в програму потрібно включити файл-заголовок iostream.h:

# Include <iostream.h>

В операціях виведення лівим операндом повинен бути потік виводу, правим операндом - виведене дане. Результатом операції висновку є потік виводу, що дозволяє записувати висновок у вигляді ланцюжка операцій <<, наприклад,

cout <<"x1 =" <<x1 <<"x2 =" <<x2 <<"\ n";

Для базових типів даних визначено їх перетворення при виведенні і точність представлення на пристрої виводу. Ці характеристики можна змінювати, застосовуючи спеціальні функції, звані маніпуляторами.

В операції введення лівим операндом повинен бути входу, а правим операндом - ім'я вводиться даного для арифметичних даних або вказівник типу char * для введення рядків, наприклад,

cin>> x1>> x2>> st;

Операції введення-виведення виконуються зліва направо і останній оператор еквівалентний оператору ((cin>> x1)>> x2)>> st; або трьом операторам

cin>> x1; cin>> x2; cin>> st;

На закінчення наведемо приклад простої програми, що запитує у користувача два цілих числа і виводить на екран їх суму:

# Include <iostream.h>

int x, y;

int main ()

{Cout <<"x ="; cin>> x; / / Запит і введення значення x

cout <<"\ ny ="; cin>> y; / / Запит і введення значення y

cout <<"\ n" <<"x + y =" <<x + y;

return 0;

}.

Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Лекція
237.9кб. | скачати


Схожі роботи:
Об`єктно орієнтоване програмування на Borland C
Об єктно-орієнтоване програмування
Об`єктно-орієнтоване середовище програмування Object Pascal в профільному курсі інформатики
Програмування мовою С з використанням об єктно орієнтованого програмування
Середовище програмування Borland Delphi
Середовище програмування Borland Delphi
Об`єктно-орієнтований підхід до програмування
Обєктно-орієнтоване програмування МП Delphi
Реалізація АВЛдеревьев через класи об`єктно програмування
© Усі права захищені
написати до нас